#include "upload.srpc.h"
#include "CryptoUtil.h"
#include "RabbitMqPro.h"
#include <workflow/MySQLResult.h>
#include <wfrest/HttpServer.h>
#include <workflow/WFFacilities.h>
#include <map>
#include <iostream>
#include <sstream>
#include <fstream>
#include <signal.h>
#include <string>
#include <functional>

using std::ostringstream;
using std::string;
using std::map;
using std::bind;
using std::function;
using std::ofstream;
using namespace wfrest;
using namespace srpc;
using namespace protocol;


static string LocalRootPath = "../file/";
static string YunRootPath = "disk/";

static RabbitMqPro _rabbitMqPro;

static WFFacilities::WaitGroup wait_group(1);

void sig_handler(int signo)
{
	wait_group.done();
}

struct FileUpLoadContext{
	UpLoadResponse * resp;
    string hashCode;
    string username;
    string filename;
    string storagepath;
    string content;
    int ufid;
    int fid; 
    bool is_insert;
    bool is_save;
};

class UpLoadServiceImpl : public UpLoad::Service
{
public:

	void upLoad(UpLoadRequest *request, UpLoadResponse *response, srpc::RPCContext *ctx) override
	{
		SeriesWork * series = ctx->get_series();
		FileUpLoadContext * context = new FileUpLoadContext{};

		
		context->resp = response;
		context->username = request->username();
		context->filename = request->filename();
		context->content = request->content();
		context->hashCode = CryptoUtil::hash_content(context->content);
		context->storagepath = LocalRootPath + context->hashCode;
		context->is_insert = false;
		
		string sql = "select * from user_file_table where username = '"+context->username+"' and filename = '"+context->filename+"';";
		
		WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&UpLoadServiceImpl::fileuploadMysqlCallback);
		mysqltask->get_req()->set_query(sql);
		series->set_context(context);
		series->push_back(mysqltask);

		series->set_callback([context](const SeriesWork*){
                delete context;
        });

	}

private:

static void fileuploadMysqlCallback(WFMySQLTask * mysqlTask){
	FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->set_success(0);
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->set_success(0);
        return ;
    }

    if(cursor.get_cursor_status()==MYSQL_STATUS_GET_RESULT){
		
		map<string,MySQLCell> row;
        cursor.fetch_row(row);
        
        if(row.size()==0){
            #ifdef DEBUG
                std::cout<<"row size = 0\n";
            #endif
            context->is_insert = true;
        }else{
            context->ufid = row["id"].as_int();
            context->is_insert = false;
        }
        string sql = "select * from file_table where hashcode = '"+context->hashCode+"';";
        WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&UpLoadServiceImpl::fileuploadMysqlCallbackUserfile);
        mysqltask->get_req()->set_query(sql);
        series_of(mysqlTask)->push_back(mysqltask);

	}else{
		context->resp->set_success(0);
	}
}

static void fileuploadMysqlCallbackUserfile(WFMySQLTask * mysqlTask)
{
	FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->set_success(0);
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->set_success(0);
        return ;
    }

    if(cursor.get_cursor_status()==MYSQL_STATUS_GET_RESULT){
        map<string,MySQLCell> row;
        cursor.fetch_row(row);
        int size = (context->content).size();
        string sql ;
        ostringstream oss ; 
        if(row.size()==0){
            context->is_save = true;
            oss<<"insert into file_table (hashcode,size) values ('"<<context->hashCode<<"',"<<size<<");";
            sql = oss.str();
            WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&UpLoadServiceImpl::fileuploadMysqlCallbackInsert);
            mysqltask->get_req()->set_query(sql);
            series_of(mysqlTask)->push_back(mysqltask);
            #ifdef DEBUG
            std::cout<<"has row size = 0\n";
            std::cout<<oss.str()<<"\n";
            #endif
            return ;
        }else{
            context->is_save = false;
            oss<<"select id from file_table where hashcode = '"<<context->hashCode<<"';";
            sql = oss.str();
            WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&UpLoadServiceImpl::fileuploadMysqlCallbackSavefile);
            mysqltask->get_req()->set_query(sql);
            series_of(mysqlTask)->push_back(mysqltask);
        }
    }
}

static void fileuploadMysqlCallbackInsert(WFMySQLTask * mysqlTask)
{
	FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->set_success(0);
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->set_success(0);
        return ;
    }
    if(cursor.get_cursor_status()==MYSQL_STATUS_OK){
        ostringstream oss ; 
        string sql;
        oss<<"select * from file_table where hashcode='"<<context->hashCode<<"';";
        sql = oss.str();
        WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&UpLoadServiceImpl::fileuploadMysqlCallbackSavefile);
        mysqltask->get_req()->set_query(sql);
        series_of(mysqlTask)->push_back(mysqltask);
    }
}

static void fileuploadMysqlCallbackSavefile(WFMySQLTask * mysqlTask)
{	
	FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->set_success(0);
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->set_success(0);
        return ;
    }

    if(cursor.get_cursor_status()==MYSQL_STATUS_GET_RESULT){
        #ifdef DEBUG
            std::cout<<"saveTask\n";
        #endif
        map<string,MySQLCell> row;
        cursor.fetch_row(row);
        context->fid = row["id"].as_int();

        if(context->is_save){
			ofstream ofs(context->storagepath);
			ofs.write(context->content.c_str(),context->content.size());
            UpLoadServiceImpl::fileuploadModifyTable(series_of(mysqlTask));
        }else{
            fileuploadModifyTable(series_of(mysqlTask));
        }
    }
}

static void fileuploadModifyTable(SeriesWork * series)
{
	
    FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series->get_context());
    
    if(context->is_save){
       _rabbitMqPro.sendMessage(context->storagepath);
    }
    
    ostringstream oss ;
    string sql ;
    if(context->is_insert){
        oss<<"insert into user_file_table (fid,username,filename) values ('"<<context->fid<<"','"<<context->username<<"','"<<context->filename<<"');";        
    }else{
        oss<<"UPDATE user_file_table SET fid = "<<context->fid<<",filename = '"<<context->filename<<"' WHERE id = "<<context->ufid<<";";
    } 
    sql = oss.str();
    WFMySQLTask * mysqltask = WFTaskFactory::create_mysql_task("mysql://root:123@localhost/newCloudDisk",3,&UpLoadServiceImpl::fileuploadMysqlCallbackResult);
    mysqltask->get_req()->set_query(sql);
    series->push_back(mysqltask);
}

static void fileuploadMysqlCallbackResult(WFMySQLTask * mysqlTask)
{
    FileUpLoadContext * context = static_cast<FileUpLoadContext*>(series_of(mysqlTask)->get_context());
    if(mysqlTask->get_state() !=WFT_STATE_SUCCESS){
        context->resp->set_success(0);
        return;
    }

    MySQLResultCursor cursor(mysqlTask->get_resp());

    if(cursor.get_cursor_status()==MYSQL_PACKET_ERROR){
        context->resp->set_success(0);
        return ;
    }
    #ifdef DEBUG
    std::cout<<"Result\n";
    #endif
    context->resp->set_success(1);
    return;
}


    
};

int main()
{
    signal(SIGINT,sig_handler); 
    GOOGLE_PROTOBUF_VERIFY_VERSION;
    unsigned short port = 1415;
    SRPCServer server;

    UpLoadServiceImpl upload_impl;
    server.add_service(&upload_impl);

    if(server.start(port) == 0){
        wait_group.wait();
        server.stop();
        google::protobuf::ShutdownProtobufLibrary();
    }else{
        std::cerr << "Error: start signserver failed!\n";
        std::exit(1);
    }

    return 0;
}
